#!/usr/bin/python

# Copyright (C) 2009 ApplianSys Ltd.
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
# MA  02110-1301, USA.

"""
Worthyd exists to make sure that one, and only one worthyd daemon thinks that
it is 'most worthy' (for each group) at any one time.

The worthyd daemon communicates with other worthyd daemons on the same subnet
via UDP packets.

It, by default, uses /etc/worthyd/worthyd.conf for callback configuration.
It generates files called /etc/worthyd/<group-name>-elected and -members,
which export the *live* state of each group.
"""


import logging
from logging.handlers import SysLogHandler
from logging import StreamHandler
from optparse import OptionParser

import ipaddr
import worthyd



if __name__ == '__main__':
    """
    This main function parses the output, creates a worthd daemon and sets
    it running.
    """

    parser = OptionParser(usage="%prog [OPTION...] ADDRESS", 
                          version="%prog 0.1",
                          )
    parser.add_option("-g", "--group", 
                      action="append",
                      dest="groups",
                      help="join a group", 
                      metavar="GROUPS",
                      )
    parser.add_option("-i", "--interval", 
                      type="int",
                      dest="interval",
                      help="minimum interval between broadcast packets",
                      metavar="INTERVAL",
                      )
    parser.add_option("-m", "--max-missing-packets",
                      type="int",
                      dest="max_missing",
                      help="number of missing packets to tolerate before "
                           "a node is removed from the group",
                      metavar="MAX_MISSING",
                      )
    parser.add_option("-b", "--backoff-algorithm",
                      dest="backoff_algorithm",
                      help="choose from %s" % (worthyd.BACKOFF_ALGORYTHMS,),
                      metavar="BACKOFF_ALGORITHM",
                      )
    parser.add_option("-w", "--worthiness-function",
                      dest="worthiness_function",
                      help="choose from %s" % (worthyd.WORTHINESS_FUNCTIONS,),
                      metavar="WORTHINESS_FUNCTION",
                      )
    parser.add_option("-v", "--verbose",
                      action="count",
                      dest="verbosity",
                      help="increase verbosity",
                      metavar="VERBOSITY",
                      )
    parser.add_option("-d", "--debug",
                      action="store_true",
                      dest="debug",
                      help="debug to console",
                      metavar="DEBUG",
                      )
    parser.add_option("-p", "--port",
                      dest="port",
                      help="port number to use",
                      metavar="PORT",
                      )
    parser.add_option("-c", "--config-dir",
                      dest="config_dir",
                      help="config directory",
                      metavar="CONFIG_DIR",
                      )
    parser.set_defaults(groups=[],
                        interval=worthyd.TEN_MILLISECONDS,
                        max_missing=worthyd.MAX_MISSING,
                        backoff_algorithm=worthyd.LOGARITHMIC,
                        worthiness_function=worthyd.LOWEST_ADDRESS,
                        port=worthyd.DEFAULT_PORT,
                        debug=False,
                        config_dir=worthyd.CONFIG_DIR,
                        )
                      
    (options, args) = parser.parse_args()
    
    # validate options
    if options.interval < 1:
        parser.error("INTERVAL must be positive and non-zero")
    if options.max_missing < 0:
        parser.error("MAX_MISSING must be positive or zero")
    for group in options.groups:
        if len(group) < 1 or len(group) > worthyd.MAX_NAME_LEN:
            parser.error("GROUPS must all be between 1 and %s characters" %
                         (MAX_NAME_LEN,))

    # validate argument
    address = None
    if len(args) != 1:
        parser.error("ADDRESS must be one of this host's IPv4 or IPv6 "
                     "addresses"
                     )

    else:
        #address = worthyd.string_to_ipaddr(args[0])
        try:
            address = ipaddr.IPv4(args[0])
        except ipaddr.IPv4IpValidationError, e:
            pass
        except ValueError, e:
            pass
        try:
            address = ipaddr.IPv6(args[0])
        except ipaddr.IPv6IpValidationError, e:
            pass
        except ValueError, e:
            pass
        if address == None:
            parser.error("ADDRESS must be one of this host's IPv4 or IPv6 "
                         "addresses"
                         )
   





    # if groups are empty, use the default group
    if options.groups == []:
        options.groups = [worthyd.DEFAULT_GROUP]

    # setup logging, linux specific
    logger = logging.getLogger("worthyd %s:%s" % (address, options.port))
    if options.verbosity != None:
        if options.verbosity == 1:
            logger.setLevel(logging.INFO)
        if options.verbosity > 1:
            logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.WARN)
    syslog = SysLogHandler(address='/dev/log')
    stderr = StreamHandler(open('/dev/stderr', 'w'))
    formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s')
    syslog.setFormatter(formatter)
    stderr.setFormatter(formatter)
    
    logger.addHandler(syslog)
    if options.debug:
        logger.addHandler(stderr)

    logger.debug("options: %s" % (options,))
    logger.debug("args: %s" % (args,))
    logger.debug("address: %s" % (address,))

    # make the daemon
    try:
        daemon = worthyd.Worthyd(group_names=options.groups, 
                                 interval=options.interval,
                                 max_missing=options.max_missing,
                                 backoff_algorithm=options.backoff_algorithm,
                                 worthiness_function=options.worthiness_function,
                                 port=options.port,
                                 address=address,
                                 config_dir=options.config_dir,
                                 logger=logger,
                                 )
      
        logger.info("starting daemon")
        daemon.start()
    except worthyd.WorthydException, e:
        # handle nicely
        logger.critical("unrecoverable error, worthyd exiting, %s", e)
    except KeyboardInterrupt, e:
        # likewise
        daemon.cleanup()
        logger.critical("daemon killed with ctrl-C")
    
                        

